package com.yuda.afterexamscore.controller;

import cn.hutool.core.collection.CollectionUtil;
import com.yuda.afterexamscore.domain.DpTotalScore;
import com.yuda.afterexamscore.domain.SpartaSubject;
import com.yuda.afterexamscore.domain.SystemVariables;
import com.yuda.afterexamscore.service.IDpTotalScoreService;
import com.yuda.afterexamscore.service.ISpartaSubjectService;
import com.yuda.afterexamscore.service.IThQuesScoreMappingService;
import com.yuda.common.core.web.controller.BaseController;
import com.yuda.common.core.web.domain.AjaxResult;
import com.yuda.common.log.annotation.Log;
import com.yuda.common.log.enums.BusinessType;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
 * 总分数（如5万个学生应该有5万条数据)Controller
 *
 * @author wyn
 * @date 2021-05-12
 */
@RestController
@RequestMapping("/dpTotalScore")
@RefreshScope
public class DpTotalScoreController extends BaseController
{
    @Autowired
    private IDpTotalScoreService dpTotalScoreService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IThQuesScoreMappingService thQuesScoreMappingService;

    @Autowired
    private ISpartaSubjectService spartaSubjectService;

    @Value("${examUUID}")
    private String examUUID;

    @Value("${reportService}")
    private String reportService;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private static Logger log = LoggerFactory.getLogger(DpTotalScoreController.class);

//    /**
//     * 查询总分数（如5万个学生应该有5万条数据)列表
//     */
//    @PreAuthorize(hasPermi = "afterexamscore:score:list")
//    @GetMapping("/list")
//    public TableDataInfo list(DpTotalScore dpTotalScore)
//    {
//        startPage();
//        List<DpTotalScore> list = dpTotalScoreService.selectDpTotalScoreList(dpTotalScore);
//        return getDataTable(list);
//    }
//
//    /**
//     * 导出总分数（如5万个学生应该有5万条数据)列表
//     */
//    @PreAuthorize(hasPermi = "afterexamscore:score:export")
//    @Log(title = "总分数（如5万个学生应该有5万条数据)", businessType = BusinessType.EXPORT)
//    @PostMapping("/export")
//    public void export(HttpServletResponse response, DpTotalScore dpTotalScore) throws IOException
//    {
//        List<DpTotalScore> list = dpTotalScoreService.selectDpTotalScoreList(dpTotalScore);
//        ExcelUtil<DpTotalScore> util = new ExcelUtil<DpTotalScore>(DpTotalScore.class);
//        util.exportExcel(response, list, "score");
//    }
//
//    /**
//     * 获取总分数（如5万个学生应该有5万条数据)详细信息
//     */
//    @PreAuthorize(hasPermi = "afterexamscore:score:query")
//    @GetMapping(value = "/{id}")
//    public AjaxResult getInfo(@PathVariable("id") Long id)
//    {
//        return AjaxResult.success(dpTotalScoreService.selectDpTotalScoreById(id));
//    }
//
//    /**
//     * 新增总分数（如5万个学生应该有5万条数据)
//     */
//    @PreAuthorize(hasPermi = "afterexamscore:score:add")
//    @Log(title = "总分数（如5万个学生应该有5万条数据)", businessType = BusinessType.INSERT)
//    @PostMapping
//    public AjaxResult add(@RequestBody DpTotalScore dpTotalScore)
//    {
//        return toAjax(dpTotalScoreService.insertDpTotalScore(dpTotalScore));
//    }
//
//    /**
//     * 修改总分数（如5万个学生应该有5万条数据)
//     */
//    @PreAuthorize(hasPermi = "afterexamscore:score:edit")
//    @Log(title = "总分数（如5万个学生应该有5万条数据)", businessType = BusinessType.UPDATE)
//    @PutMapping
//    public AjaxResult edit(@RequestBody DpTotalScore dpTotalScore)
//    {
//        return toAjax(dpTotalScoreService.updateDpTotalScore(dpTotalScore));
//    }
//
//    /**
//     * 删除总分数（如5万个学生应该有5万条数据)
//     */
//    @PreAuthorize(hasPermi = "afterexamscore:score:remove")
//    @Log(title = "总分数（如5万个学生应该有5万条数据)", businessType = BusinessType.DELETE)
//	@DeleteMapping("/{ids}")
//    public AjaxResult remove(@PathVariable Long[] ids)
//    {
//        return toAjax(dpTotalScoreService.deleteDpTotalScoreByIds(ids));
//    }

    @ApiOperation("从dp_subject_score里把数据插入到dp_total_score,每次执行会把dp_total_score数据清空,使用中!!!!!!!!!!!!!!!!!!!")
    @Log(title = "insertFromDpSubjectScore", businessType = BusinessType.INSERT)
//    @PreAuthorize(hasRole = "admin")
    @PostMapping("/insertFromDpSubjectScore")
    public AjaxResult insertFromDpQuesScore() throws InterruptedException {
        //计算时间:初始时间
        Instant insOne = Instant.now();
        //2021年6月9日新增，添加一个开关，让死锁接口只能存在一个
        //如果能同行，关闭开关
        SystemVariables.InterfaceSwitch = false;
        SystemVariables.msg = "生成小题分表和科目表中，请稍等";
        try {
            //插入dp_total_score,判断redis里除了非必要科目所有科目是否完成,完成了再执行插入
            //1.查询redis里的状态
            //1.1查询所有科目,遍历科目查询redis的状态
            //redis里查有咩有总分要去除的科目,没有则传null
            Boolean remove = redisTemplate.hasKey(examUUID + "RemoveSubject");
           // List<SpartaSubject> spartaSubjects = spartaSubjectService.selectSpartaSubjectList(null);
            List<SpartaSubject> spartaSubjects = jdbcTemplate.query("select  distinct  subject_id from dp_ques_score",new BeanPropertyRowMapper<>(SpartaSubject.class));
            //排除删除科目
            if (remove){
                //有,则排除插入
                Object removeSubject = redisTemplate.opsForValue().get(examUUID + "RemoveSubject");
                String removeSubjectStr = removeSubject.toString();
                spartaSubjects = spartaSubjects.stream().filter(s -> !removeSubjectStr.contains(s.getRelationSubjectId())).collect(Collectors.toList());
            }
            for (SpartaSubject spartaSubject : spartaSubjects) {
                Object formalAnswer = redisTemplate.opsForHash().get(examUUID + "ProcessOf" + spartaSubject.getRelationSubjectId(), "formalAnswer");
                Object quesScore = redisTemplate.opsForHash().get(examUUID + "ProcessOf" + spartaSubject.getRelationSubjectId(), "quesScore");
                Object subjectScore = redisTemplate.opsForHash().get(examUUID + "ProcessOf" + spartaSubject.getRelationSubjectId(), "subjectScore");
//                if (formalAnswer.toString().equals("0")||quesScore.toString().equals("0")||subjectScore.toString().equals("0")){
//                    SystemVariables.InterfaceSwitch = true;
//                    return new AjaxResult(304, spartaSubject.getDesc()+":"+spartaSubject.getName()+"有状态未完成,需要先完成");
//                }
            }
            //3.完成了,先删除后插入
            log.info("删除dp_total_score中");
            dpTotalScoreService.truncateTable();

            log.info("插入dp_total_score中");
            //为之后检查总分上线做准备
            String subjectRemove = "1013";
            if (remove){
                //有,则排除插入
                Object removeSubject = redisTemplate.opsForValue().get(examUUID + "RemoveSubject");
                dpTotalScoreService.insertFromDpSubjectScore(removeSubject.toString().split(","));
                subjectRemove = removeSubject.toString();
            }else {
                //无,则传空值
                dpTotalScoreService.insertFromDpSubjectScore(null);
            }
            //检查总数是否正确
            String dqsC = jdbcTemplate.queryForObject("SELECT count(1) c from (SELECT 1 from dp_ques_score where subject_relation_id not in (?) GROUP BY zkz_num ) a",new Object[]{subjectRemove}, String.class);
            String dtsC = jdbcTemplate.queryForObject("SELECT count(1) c from dp_total_score", String.class);
            if (!dqsC.equals(dtsC)){
                SystemVariables.InterfaceSwitch = true;
                dpTotalScoreService.truncateTable();
                return new AjaxResult(304, "dp_total_score表总数与dp_ques_score group by后数据不一致");
            }
            //检查总分有没有超过总分上限---2021年6月5日新增    TODO 有问题，需改进
            //查询所有pg_type的总分上线是多少，再筛选是否有学生成绩超过总分
//            List<SpartaSubject> paperSubject = jdbcTemplate.query("SELECT stu_type gaokaoType,GROUP_CONCAT(relation_subject_id separator '#') relationSubjectId from sparta_stu_paper_subject GROUP BY stu_type", new BeanPropertyRowMapper<>(SpartaSubject.class));
//            if (CollectionUtil.isNotEmpty(paperSubject)){
//                //循环，把各科分数加上把总分放在desc里
//                for (SpartaSubject spartaSubject : paperSubject) {
//                    String relationSubjectId = spartaSubject.getRelationSubjectId().replace('#',',');
//                    if (remove){
//                        //有,则排除插入
//                        Object removeSubject = redisTemplate.opsForValue().get(examUUID + "RemoveSubject");
//                        String removeSubjectStr = removeSubject.toString();
//                        String[] removeArr = removeSubjectStr.split(",");
//                        for (String s : removeArr) {
//                            if (relationSubjectId.contains(s)){
//                                relationSubjectId = relationSubjectId.replace(s,"1");   //将subjectId换成没用的1 可以减少处理，问题
//                            }
//                        }
//                    }
//                    List<String> list = jdbcTemplate.queryForList("SELECT SUM(full_score) fullScore from sparta_subject where  relation_subject_id in ("+relationSubjectId+") and relation_subject_id not like '%#%'", String.class);
//                    if (CollectionUtil.isNotEmpty(list)){
//                        spartaSubject.setFullScore(list.get(0));
//                    }
//                }
//            }
//            //将paperSubject转化为Map
//            Map<Integer, Integer> pgTypeMappuing = new HashMap<>();
//            for (SpartaSubject spartaSubject : paperSubject) {
//                pgTypeMappuing.put(Integer.parseInt(spartaSubject.getGaokaoType()),Integer.parseInt(spartaSubject.getFullScore()));
//            }
//            //查询所有学生总分
//            List<DpTotalScore> dpTotalScores = jdbcTemplate.query("SELECT student_id,pg_type,score from dp_total_score", new BeanPropertyRowMapper<>(DpTotalScore.class));
//            for (DpTotalScore dpTotalScore : dpTotalScores) {
//                Integer pgType = dpTotalScore.getPgType();
//                Integer fullScore = pgTypeMappuing.get(pgType);
//                if (Integer.parseInt(dpTotalScore.getScore())  > fullScore){
//                    SystemVariables.InterfaceSwitch = true;
//                    dpTotalScoreService.truncateTable();
//                    return new AjaxResult(304, dpTotalScore.getStudentId()+"总分大于上限");
//                }
//            }

//            List<DpTotalScoreVo> dpTotalScoreVos = jdbcTemplate.query("SELECT student_id,pg_type,gaige,score,sum(c.full_score+0) sumScore\n" +
//                    "from dp_total_score a\n" +
//                    "LEFT JOIN sparta_stu_paper_subject b on a.pg_type = b.stu_type\n" +
//                    "LEFT JOIN sparta_subject c on b.relation_subject_id = c.relation_subject_id and b.subject_id = c.id and c.relation_subject_id not like '%#%'\n" +
//                    "where c.relation_subject_id not in (1013,1014,?)\n" +
//                    "GROUP BY a.student_id\n" +
//                    "having score > sumScore", new Object[]{subjectRemove}, new BeanPropertyRowMapper<>(DpTotalScoreVo.class));
//            if (CollectionUtil.isNotEmpty(dpTotalScoreVos)){
//                StringBuilder stringBuilder = new StringBuilder();
//                dpTotalScoreVos.stream().forEach(d -> stringBuilder.append(d.getStudentId()+"学生，总分："+d.getScore()+",上限："+d.getSumScore()+";"));
//                SystemVariables.InterfaceSwitch = true;
//                return new AjaxResult(304, stringBuilder.toString());
//            }
            log.info("更新dp_total_score表信息");
        }catch (Exception e){
            SystemVariables.InterfaceSwitch = true;
            e.printStackTrace();
            return new AjaxResult(304, "未知错误，请查看日志");
        }

//        ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
//        CountDownLatch latch4 = new CountDownLatch(4);
//        Runnable task11 = new DpTotalScoreController.AutomaticProcessing(latch4, "http://"+reportService+"/task/dpTotalScore_country2020");
//        Runnable task12 = new DpTotalScoreController.AutomaticProcessing(latch4, "http://"+reportService+"/task/dpTotalScore_province2020");
//        Runnable task13 = new DpTotalScoreController.AutomaticProcessing(latch4, "http://"+reportService+"/rankx/totalscore");
//        Runnable task14 = new DpTotalScoreController.AutomaticProcessing(latch4, "http://"+reportService+"/rankP/totalRank");
//        singleExecutor.execute(task11);
//        singleExecutor.execute(task12);
//        singleExecutor.execute(task13);
//        singleExecutor.execute(task14);
//        latch4.await();
//        //计算时间:结束时间
//        singleExecutor.shutdown();
        //计算时间:结束时间
        Instant insTwo = Instant.now();
        SystemVariables.InterfaceSwitch = true;
        return AjaxResult.success("Difference in seconds(秒) : " + Duration.between(insOne, insTwo).getSeconds());
    }

    @ApiOperation("查询总分表状态")
    @PostMapping("/queryTotalScoreState")
//    @PreAuthorize(hasRole = "admin")
    public AjaxResult queryTotalScoreState() {
        //redis里查有咩有总分要去除的科目,没有则传null
        Boolean remove = redisTemplate.hasKey(examUUID + "RemoveSubject");
        String subjectRemove = "1013";
        if (remove){
            //有,则排除插入
            Object removeSubject = redisTemplate.opsForValue().get(examUUID + "RemoveSubject");
            subjectRemove = removeSubject.toString();
        }
        String count = jdbcTemplate.queryForObject("SELECT count(1) c from ( select student_id from tsinghua_formal_answer where subject_relation_id not in (?) group by student_id ) a",new Object[]{subjectRemove}, String.class);
        List<String> countTotal = jdbcTemplate.queryForList("select  count(1) c from dp_total_score", String.class);
        if (CollectionUtil.isEmpty(countTotal)){
            return AjaxResult.success ("未完成");
        }
        String counts = countTotal.get(0);
        if (count!= null && counts!=null && count.equals(counts)){
            return AjaxResult.success("已完成");
        }else {
            return AjaxResult.success ("未完成");
        }
    }

    @GetMapping("/test")
    public AjaxResult test(){
        try {
            Boolean remove = redisTemplate.hasKey(examUUID + "RemoveSubject");
            //检查总分有没有超过总分上限---2021年6月5日新增    TODO 有问题，需改进
            //查询所有pg_type的总分上线是多少，再筛选是否有学生成绩超过总分
            List<SpartaSubject> paperSubject = jdbcTemplate.query("SELECT stu_type gaokaoType,GROUP_CONCAT(relation_subject_id separator '#') relationSubjectId from sparta_stu_paper_subject GROUP BY stu_type", new BeanPropertyRowMapper<>(SpartaSubject.class));
            if (CollectionUtil.isNotEmpty(paperSubject)){
                //循环，把各科分数加上把总分放在desc里
                for (SpartaSubject spartaSubject : paperSubject) {
                    String relationSubjectId = spartaSubject.getRelationSubjectId().replace('#',',');
                    if (remove){
                        //有,则排除插入
                        Object removeSubject = redisTemplate.opsForValue().get(examUUID + "RemoveSubject");
                        String removeSubjectStr = removeSubject.toString();
                        String[] removeArr = removeSubjectStr.split(",");
                        for (String s : removeArr) {
                            if (relationSubjectId.contains(s)){
                                relationSubjectId = relationSubjectId.replace(s,"1");   //将subjectId换成没用的1 可以减少处理，问题
                            }
                        }
                    }
                    String list = jdbcTemplate.queryForObject("SELECT SUM(full_score) fullScore from sparta_subject where  relation_subject_id in ("+relationSubjectId+") and relation_subject_id not like '%#%'", String.class);
//                if (CollectionUtil.isNotEmpty(list)){
                    spartaSubject.setFullScore(list);
//                }
                }
            }
            //将paperSubject转化为Map
            Map<Integer, Integer> pgTypeMappuing = new HashMap<>();
            for (SpartaSubject spartaSubject : paperSubject) {
                pgTypeMappuing.put(Integer.parseInt(spartaSubject.getGaokaoType()),Integer.parseInt(spartaSubject.getFullScore()));
            }
            //查询所有学生总分
            List<DpTotalScore> dpTotalScores = jdbcTemplate.query("SELECT student_id,pg_type,score from dp_total_score", new BeanPropertyRowMapper<>(DpTotalScore.class));
            for (DpTotalScore dpTotalScore : dpTotalScores) {
                Integer pgType = dpTotalScore.getPgType();
                Integer fullScore = pgTypeMappuing.get(pgType);
                if (Integer.parseInt(dpTotalScore.getScore())  > fullScore){
                    SystemVariables.InterfaceSwitch = true;
                    return new AjaxResult(304, dpTotalScore.getStudentId()+"总分大于上限");
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            return new AjaxResult(304,"测试嘛，报错难免的");
        }
        return AjaxResult.success();
    }


    class AutomaticProcessing implements Runnable{
        private CountDownLatch threadsSignal;
        private String url;

        public AutomaticProcessing(CountDownLatch threadsSignal, String url) {
            this.threadsSignal = threadsSignal;
            this.url = url;
        }

        @Override
        public void run() {
            try {
                connectPOST(url);
            } catch (IOException e) {
                System.out.println("发送 POST 请求出现异常！" + e);
                e.printStackTrace();
            }
            threadsSignal.countDown();//必须等核心处理逻辑处理完成后才可以减1
            log.info(Thread.currentThread().getName() + "结束. 还有"+ threadsSignal.getCount() + " 个线程 ");
        }

        public String connectPOST(String urlStr) throws IOException {
            InputStream ins = null;
            try {
                URL url = new URL(urlStr);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("POST");//POST
                conn.setConnectTimeout(1800000000);
                conn.setDoInput(true);
                conn.setDoOutput(true);
                BufferedWriter ous = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(), "utf-8"));
                //ous.writeChars(params);
                ous.flush();
                conn.connect();
                int code = conn.getResponseCode();
                if (code == HttpURLConnection.HTTP_OK || code == 400) {
                    ins = conn.getInputStream();
                    return readInputStream(ins);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } finally {
                if (ins != null) {
                    ins.close();
                }
            }
            return null;
        }

        public String readInputStream(InputStream ins) {
            if (ins == null) {
                return null;
            }
            BufferedInputStream bis = new BufferedInputStream(ins);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            try {
                byte[] buffer = new byte[128];
                int n = -1;
                while ((n = bis.read(buffer)) != -1) {
                    bos.write(buffer, 0, n);
                }
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            } finally {
                if (bis != null) {
                    try {
                        bis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return new String(bos.toByteArray());
        }
    }
}
